package com.panlin.lib_guoxin;

import android.os.Build;
import android.util.Log;

import com.gg.reader.api.dal.GClient;
import com.gg.reader.api.protocol.gx.EnumG;
import com.gg.reader.api.protocol.gx.LogBaseEpcInfo;
import com.gg.reader.api.protocol.gx.Message;
import com.gg.reader.api.protocol.gx.MsgBaseDestroyEpc;
import com.gg.reader.api.protocol.gx.MsgBaseGetBaseband;
import com.gg.reader.api.protocol.gx.MsgBaseGetFreqRange;
import com.gg.reader.api.protocol.gx.MsgBaseGetPower;
import com.gg.reader.api.protocol.gx.MsgBaseInventoryEpc;
import com.gg.reader.api.protocol.gx.MsgBaseLockEpc;
import com.gg.reader.api.protocol.gx.MsgBaseSetBaseband;
import com.gg.reader.api.protocol.gx.MsgBaseSetFreqRange;
import com.gg.reader.api.protocol.gx.MsgBaseSetPower;
import com.gg.reader.api.protocol.gx.MsgBaseStop;
import com.gg.reader.api.protocol.gx.MsgBaseWriteEpc;
import com.gg.reader.api.protocol.gx.ParamEpcFilter;
import com.gg.reader.api.protocol.gx.ParamEpcReadEpc;
import com.gg.reader.api.protocol.gx.ParamEpcReadReserved;
import com.gg.reader.api.protocol.gx.ParamEpcReadTid;
import com.gg.reader.api.protocol.gx.ParamEpcReadUserdata;
import com.gg.reader.api.utils.HexUtils;
import com.gg.reader.api.utils.StringUtils;
import com.sum.common.bean.ReadMode;
import com.sum.common.bean.ReaderExt;
import com.sum.common.bean.Stats;
import com.sum.common.bean.TagInfo;
import com.sum.common.bean.WriteParams;
import com.sum.common.inter.IUHFManager;
import com.sum.common.inter.OnInventoryListener;
import com.sum.common.utils.DataUtils;
import com.sum.common.utils.LogU;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

public class GuoxinUHFManager implements IUHFManager {
    private static GuoxinUHFManager mInstance;
    private GClient client;
    private OnInventoryListener tagListener;
    private ParamEpcFilter currentFilter;
    private boolean isConnect;

    private boolean isRunning;

    private int maxSerialNumber;
    private int totalCount;
    private List<TagInfo> tagList; //获取标签数据
    private int tagCountPerSecond;

    private static final int DEFAULT_TIMEOUT = 5000; // 默认超时时间
    private final ArrayList<LogBaseEpcInfo> epcList = new ArrayList<>();
    private int mCurRegion = 2;
    MsgBaseInventoryEpc msg = new MsgBaseInventoryEpc();
    private long startTime = System.currentTimeMillis();

    private GuoxinUHFManager() {
        client = new GClient();
        this.tagList = Collections.synchronizedList(new ArrayList<>());
        registerCallBack();
    }

    @Override
    public IUHFManager getInstance() {
        if (mInstance == null) {
            synchronized (GuoxinUHFManager.class) {
                if (mInstance == null) {
                    mInstance = new GuoxinUHFManager();
                }
            }
        }
        return mInstance;
    }

    // 基础工具方法
    private boolean sendMsg(Message msg) {
        if (client != null) {
            client.sendSynMsg(msg);
            return msg.getRtCode() == 0;
        }
        return false;
    }

    private ReaderExt.READER_ERR convertError(boolean success) {
        return success ? ReaderExt.READER_ERR.MT_OK_ERR : ReaderExt.READER_ERR.MT_OTHER_ERR;
    }

    // 连接管理
    @Override
    public boolean connect(String port, int baudRate) {
        if (isConnect) {
            return true;
        }
        // 打开串口连接
        try {
            LogU.d("connect port = " + port + ", baudRate = " + baudRate);
            boolean connected = client.openCusAndroidSerial(port + ":" + baudRate, 64, 100);
            LogU.d("connect connected = " + connected);
            if (connected) {
                MsgBaseStop msgBaseStop = new MsgBaseStop();
                connected = sendMsg(msgBaseStop);
                if (connected) {
                    isConnect = true;
                    return true;
                }
            }else {
                close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Error error){
            error.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean isConnected() {
        return isConnect;
    }

    @Override
    public boolean close() {
        isConnect = false;
        stopTagInventory();
        if (client != null) {
            client.close();
            client = null;
        }
        return true;
    }

    // 事件处理
    private void registerCallBack() {
        if (client != null) {
            // 标签数据回调
            client.onTagEpcLog = (s, info) -> {
                if (info.getResult() == 0) {
                    LogU.d("logBaseEpcInfo = " + info.getEpc()+" ,tid:"+info.getTid());
                    processTagInfo(info);
                    if (tagListener != null) {
                        LogU.d("tagListener",tagListener);
                        tagListener.onInventoryListener(tagList, new Stats(tagCountPerSecond,maxSerialNumber,totalCount));
                    }
                }
            };

            // 读取结束回调
            client.onTagEpcOver = (s, logBaseEpcOver) -> {

            };
        }
    }

    private void processTagInfo(LogBaseEpcInfo baseEpcInfo) {
        String epcId = baseEpcInfo.getEpc();
        int rssi = baseEpcInfo.getRssi();
        String tid = baseEpcInfo.getTid();
        String user = baseEpcInfo.getUserdata();
        String rever = baseEpcInfo.getReserved();

        synchronized (tagList) {
            int findIndex = -1;
            for (int i = 0; i < tagList.size(); i++) {
                if (tagList.get(i).getEpc().equals(epcId)) {
                    findIndex = i;
                    break;
                }
            }

            if (findIndex != -1) {
                TagInfo tagInfo = tagList.get(findIndex);
                tagInfo.setCount(tagInfo.getCount() + 1);
                tagInfo.setRssi(rssi);
                tagInfo.setTid(tid);
                tagInfo.setUserData(user);
                tagInfo.setReservedData(rever);
                // 替换原位置
                tagList.set(findIndex, tagInfo);
            } else {
                TagInfo newTag = new TagInfo();
                newTag.setEpc(epcId);
                newTag.setCount(1);
                newTag.setRssi(rssi);
                newTag.setTid(tid);
                newTag.setUserData(user);
                newTag.setReservedData(rever);
                newTag.setSerialNumber(tagList.size() + 1);
                tagList.add(newTag);
            }

            maxSerialNumber = tagList.size();

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                totalCount = tagList.stream().mapToInt(TagInfo::getCount).sum();
            }

            // 获取每秒读取张数
            int seconds = (int) ((System.currentTimeMillis() - startTime) / 1000);
            if (seconds > 1) {
                tagCountPerSecond = totalCount / seconds;
            }


            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                Collections.sort(tagList, Comparator.comparingInt(TagInfo::getSerialNumber));
            }

            if (tagListener != null) {
                tagListener.onInventoryListener(tagList,
                        new Stats(tagCountPerSecond, maxSerialNumber, totalCount));
            }
        }
    }



    @Override
    public void setOnInventoryListener(OnInventoryListener listener) {
        this.tagListener = listener;
    }

    // 标签操作相关方法
    @Override
    public ReaderExt.READER_ERR asyncStartReading(ReadMode readMode) {
        isRunning = true;
        startTime = System.currentTimeMillis();
        msg.setAntennaEnable(EnumG.AntennaNo_1);
        msg.setInventoryMode(EnumG.InventoryMode_Inventory);
        if (msg!= null) {
            msg.setFilter(currentFilter);
        }
        return convertError(sendMsg(msg));
    }

    @Override
    public ReaderExt.READER_ERR asyncStopReading() {
        isRunning = false;
        return convertError(sendMsg(new MsgBaseStop()));
    }

    @Override
    public void clearTagList() {
        // 清空标签缓存
        tagCountPerSecond = 0;
        if(!tagList.isEmpty()){
            tagList.clear();
        }
        maxSerialNumber=0;
        totalCount=0;
    }

    @Override
    public List<TagInfo> tagInventoryRealTime() {
        List<LogBaseEpcInfo> logBaseEpcInfos = readList(300);
        List<TagInfo> lists = new ArrayList<TagInfo>();
        for (int i=0;i<logBaseEpcInfos.size();i++){
            TagInfo tagInfo = new TagInfo();
            tagInfo.setEpc(logBaseEpcInfos.get(i).getEpc());
            tagInfo.setTid(logBaseEpcInfos.get(i).getTid());
            tagInfo.setRssi(logBaseEpcInfos.get(i).getRssi());
            lists.add(tagInfo);
        }
        return lists; // 实时盘存,由监听器处理
    }

    private List<LogBaseEpcInfo> readList(int timeout) {
        epcList.clear();
        msg.setAntennaEnable(EnumG.AntennaNo_1);
        msg.setInventoryMode(EnumG.InventoryMode_Inventory);
        client.sendSynMsg(msg);
        if (0x00 == msg.getRtCode()) {
            System.out.println("MsgBaseInventoryEpc[OK].");
            synchronized (epcList) {
                try {
                    epcList.wait(timeout);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            sendMsg(new MsgBaseStop());
        } else {
            System.out.println("启动读卡失败" + msg.getRtMsg());
        }
        return epcList;
    }

    @Override
    public boolean stopTagInventory() {
        return sendMsg(new MsgBaseStop());
    }

    @Override
    public List<TagInfo> tagInventoryByTimer(short readtime) {
        return null; // 定时盘存,由监听器处理
    }

    public ReaderExt.READER_ERR writeTagData(
            WriteParams writeParams,
            int mbank,
            int startAddress,
            String oldData,
            byte[] data,
            int dataLen,
            byte[] accessPasswd,
            short timeout
    ) {
        MsgBaseWriteEpc msg = new MsgBaseWriteEpc();
        msg.setAntennaEnable(EnumG.AntennaNo_1);
        msg.setArea(convertBank(mbank));
        msg.setStart(startAddress);
        msg.setHexPassword(HexUtils.bytes2HexString(accessPasswd));
        String hexStr;

        if (mbank == 1) {
            int epcLength = ComputedPc.getEPCLength(writeParams.getWriteData());
            hexStr = ComputedPc.getPc(epcLength)
                    + DataUtils.padLeft(writeParams.getWriteData().trim().toUpperCase(), 4 * epcLength, '0');
            msg.setBwriteData(HexUtils.hexString2Bytes(hexStr));
        } else if (mbank == 3) {
            String userData = writeParams.getWriteData().trim();
            int length = ComputedPc.getEPCLength(writeParams.getWriteData());
            String s = DataUtils.padLeft(userData, 4 * length, '0');
            if (!StringUtils.isNullOfEmpty(s)) {
                msg.setBwriteData((HexUtils.hexString2Bytes(s)));
            }
        } else {
            hexStr = HexUtils.bytes2HexString(data);
            msg.setBwriteData(HexUtils.hexString2Bytes(hexStr));
        }
        setFilter(mbank, oldData, msg);

        boolean success = sendMsg(msg);
        if (!success) {
            LogU.d("writeTagData error = " + msg.getRtMsg());
        }

        return convertError(success);
    }

    private void setFilter(int bank, String oldData, MsgBaseWriteEpc msg) {
        if (StringUtils.isNullOfEmpty(oldData)) return;

        ParamEpcFilter filter = new ParamEpcFilter();

        switch (bank) {
            case 1:
                filter.setArea(EnumG.ParamFilterArea_EPC);
                filter.setBitStart(32);
                break;
            case 2:
                filter.setArea(EnumG.ParamFilterArea_TID);
                filter.setBitStart(0);
                break;
            case 3:
                filter.setArea(EnumG.ParamFilterArea_Userdata);
                filter.setBitStart(0);
                break;
            default:
                return;
        }

        filter.setHexData(oldData.trim());
        filter.setBitLength(oldData.length() * 4);
        msg.setFilter(filter);
    }


    @Override
    public ReaderExt.READER_ERR writeTagDataByFilter(char mbank, int startaddress, byte[] data,
                                                     int datalen, byte[] accesspasswd, short timeout,
                                                     byte[] fdata, int fbank, int fstartaddr, boolean matching) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public ReaderExt.READER_ERR writeTagEPC(byte[] data, byte[] accesspwd, short timeout) {
        MsgBaseWriteEpc msg = new MsgBaseWriteEpc();
        msg.setAntennaEnable(EnumG.AntennaNo_1);
        msg.setArea(EnumG.WriteArea_Epc);
        msg.setStart(1);
        msg.setBwriteData(data);
        msg.setHexPassword(HexUtils.bytes2HexString(accesspwd));
        if (currentFilter != null) {
            msg.setFilter(currentFilter);
        }
        return convertError(sendMsg(msg));
    }

    @Override
    public ReaderExt.READER_ERR writeTagEPCByFilter(byte[] data, byte[] accesspwd, short timeout,
                                                    byte[] fdata, int fbank, int fstartaddr, boolean matching) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    // 读取操作
    @Override
    public ReaderExt.READER_ERR getTagData(int mbank, int startaddr, int len, byte[] rdata,
                                           byte[] password, short timeout) {
        msg.setAntennaEnable(EnumG.AntennaNo_1);

        switch (mbank) {
            case 0: // Reserved
                ParamEpcReadReserved readReserved=new ParamEpcReadReserved();
                readReserved.setStart(startaddr);
                readReserved.setLen(len);
                msg.setReadReserved(readReserved);
                break;
            case 1: // Epc
                ParamEpcReadEpc param = new ParamEpcReadEpc();
                param.setStart(startaddr);
                param.setLen(len);
                msg.setReadEpc(param);
                break;
            case 2: // Tid
                ParamEpcReadTid param2 = new ParamEpcReadTid();
                param2.setMode(EnumG.ParamTidMode_Fixed);
                param2.setLen(len);
                msg.setReadTid(param2);
                break;
            case 3: // USER
                ParamEpcReadUserdata param3 = new ParamEpcReadUserdata();
                param3.setStart(startaddr);
                param3.setLen(len);
                msg.setReadUserdata(param3);
                break;
        }

        msg.setHexPassword(HexUtils.bytes2HexString(password));
        if (currentFilter != null) {
            msg.setFilter(currentFilter);
        }

        return convertError(sendMsg(msg));
    }

    @Override
    public byte[] getTagDataByFilter(int mbank, int startaddr, int len, byte[] password,
                                     short timeout, byte[] fdata, int fbank, int fstartaddr, boolean matching) {
        return null;
    }

    // 过滤设置
    @Override
    public boolean setInventoryFilter(byte[] fdata, int fbank, int fstartaddr, boolean matching) {
        currentFilter = new ParamEpcFilter();
        currentFilter.setArea(convertBank(fbank));
        currentFilter.setBitStart(fstartaddr * 8);
        currentFilter.setHexData(HexUtils.bytes2HexString(fdata));
        currentFilter.setBitLength(fdata.length * 8);
        return true;
    }

    @Override
    public boolean setCancleInventoryFilter() {
        currentFilter = null;
        return true;
    }

    // 锁定操作
    @Override
    public ReaderExt.READER_ERR lockTag(ReaderExt.Lock_Obj lockobject, ReaderExt.Lock_Type locktype,
                                        byte[] accesspasswd, int timeout) {
        MsgBaseLockEpc msg = new MsgBaseLockEpc();
        msg.setAntennaEnable(EnumG.AntennaNo_1);

        // 设置锁定区域
        mCurRegion = convertLockObject(lockobject);
        msg.setArea(mCurRegion);

        // 设置锁定类型
        msg.setMode(convertLockType(locktype));

        // 设置访问密码
        msg.setHexPassword(HexUtils.bytes2HexString(accesspasswd));

        // 设置过滤条件
        if (currentFilter != null) {
            msg.setFilter(currentFilter);
        } else {
            LogU.e("currentFilter is null, may cause lock failure");
        }

        return convertError(sendMsg(msg));
    }


    @Override
    public ReaderExt.READER_ERR lockTagByFilter(ReaderExt.Lock_Obj lockobject,
                                                ReaderExt.Lock_Type locktype, byte[] accesspasswd, short timeout,
                                                byte[] fdata, int fbank, int fstartaddr, boolean matching) {
        return ReaderExt.READER_ERR.MT_OK_ERR;
    }

    @Override
    public ReaderExt.READER_ERR killTag(byte[] killpasswd, int timeout) {
        MsgBaseDestroyEpc msg = new MsgBaseDestroyEpc();
        msg.setAntennaEnable(EnumG.AntennaNo_1);
        msg.setHexPassword(HexUtils.bytes2HexString(killpasswd));
        if (currentFilter != null) {
            msg.setFilter(currentFilter);
        }
        return convertError(sendMsg(msg));
    }

    @Override
    public ReaderExt.READER_ERR killTagByFilter(byte[] killpasswd, int timeout,
                                                byte[] fdata, int fbank, int fstartaddr, boolean matching) {
        return ReaderExt.READER_ERR.MT_OK_ERR;
    }

    // 参数设置
    @Override
    public ReaderExt.READER_ERR setPower(int power) {
        MsgBaseSetPower msg = new MsgBaseSetPower();
        Hashtable<Integer, Integer> powerMap = new Hashtable<>();
        powerMap.put(1, power);
        msg.setDicPower(powerMap);
        return convertError(sendMsg(msg));
    }

    @Override
    public int[] getPower() {
        MsgBaseGetPower msg = new MsgBaseGetPower();
        if (sendMsg(msg)) {
            Map<Integer, Integer> powerMap = msg.getDicPower();
            Integer power = powerMap.get(1);
            return new int[]{power != null ? power : 0};
        }
        return null;
    }

    @Override
    public ReaderExt.READER_ERR setRegion(int region) {
        MsgBaseSetFreqRange msg = new MsgBaseSetFreqRange();
        msg.setFreqRangeIndex(region);
        return convertError(sendMsg(msg));
    }

    @Override
    public ReaderExt.Region_Conf getRegion() {
        MsgBaseGetFreqRange msg = new MsgBaseGetFreqRange();
        if (sendMsg(msg)) {
            return convertRegion(msg.getFreqRangeIndex());
        }
        return null;
    }

    @Override
    public ReaderExt.READER_ERR setQValue(int qValue) {
        MsgBaseSetBaseband msg = new MsgBaseSetBaseband();
        msg.setqValue(qValue);
        return convertError(sendMsg(msg));
    }

    @Override
    public int getQValue() {
        MsgBaseGetBaseband msg = new MsgBaseGetBaseband();
        if (sendMsg(msg)) {
            return msg.getqValue();
        }
        return 0;
    }

    // Getter/Setter和其他未实现的方法
    @Override
    public String getSession() {
        return "";
    }

    @Override
    public String getFlag() {
        return "";
    }

    @Override
    public int getProtocol() {
        return 0;
    }

    @Override
    public ReaderExt.READER_ERR setProtocol(String protocol) {
        return ReaderExt.READER_ERR.MT_OK_ERR;
    }

    @Override
    public int[] getFrequencyPoints() {
        return null;
    }

    @Override
    public ReaderExt.READER_ERR setFrequencyPoints(int[] frequencyPoints) {
        return ReaderExt.READER_ERR.MT_OK_ERR;
    }

    @Override
    public ReaderExt.READER_ERR setFastMode() {
        return ReaderExt.READER_ERR.MT_OK_ERR;
    }

    @Override
    public int getTemperature() {
        return 0;
    }

    @Override
    public String getHardware() {
        return null;
    }

    @Override
    public ReaderExt.READER_ERR getLastDetailError(ReaderExt.ErrInfo info) {
        return ReaderExt.READER_ERR.MT_OK_ERR;
    }

    @Override
    public boolean setInventoryParameter(Map<String, Object> params) {
        return true;
    }

    @Override
    public String getInventoryParameter() {
        return "";
    }

    @Override
    public void setSingleLabelMode(boolean enabled) {
    }

    @Override
    public void setGroupTidMode(boolean enabled) {
        msg.setAntennaEnable(EnumG.AntennaNo_1);
        ParamEpcReadTid tidParam = new ParamEpcReadTid();
        tidParam.setMode(0);
        tidParam.setLen(6);

        if (enabled) {
            if (msg != null) {
                msg.setReadTid(tidParam);
            }
        } else {
            if (msg != null) {
                msg.setReadTid(null);
            }
        }
    }


    // 工具转换方法
    private int convertBank(int bank) {
        switch (bank) {
            case 0:
                return EnumG.WriteArea_Reserved;
            case 2:
                return EnumG.WriteArea_Tid;
            case 3:
                return EnumG.WriteArea_Userdata;
            default:
                return EnumG.WriteArea_Epc;
        }
    }

    // 续上文的convertLockObject方法
    private int convertLockObject(ReaderExt.Lock_Obj lockObject) {
        switch (lockObject) {
            case LOCK_OBJECT_KILL_PASSWORD:
                return 0;
            case LOCK_OBJECT_ACCESS_PASSWD:
                return 1;
            case LOCK_OBJ_EPC_MEMORY:
                return 2;
            case LOCK_OBJECT_TID_MEMORY:
                return 3;
            case LOCK_OBJECT_USER_MEMORY:
                return 4;
            default:
                return 4;
        }
    }

    private int convertLockType(ReaderExt.Lock_Type lockType) {
        switch (lockType) {
            case ACCESS_PASSWD_UNLOCK:
            case KILL_PASSWORD_UNLOCK:
            case BANK1_UNLOCK:
            case BANK2_UNLOCK:
            case BANK3_UNLOCK:
                return 0;
            case ACCESS_PASSWD_LOCK:
            case KILL_PASSWORD_LOCK:
            case BANK1_LOCK:
            case BANK2_LOCK:
            case BANK3_LOCK:
                return 1;
            case ACCESS_PASSWD_PERM_LOCK:
            case KILL_PASSWORD_PERM_LOCK:
            case BANK1_PERM_LOCK:
            case BANK2_PERM_LOCK:
            case BANK3_PERM_LOCK:
                return 2;
            default:
                return 0;
        }
    }


    private ReaderExt.Region_Conf convertRegion(int index) {
        switch (index) {
            case 0:
                return ReaderExt.Region_Conf.RG_PRC;
            case 1:
                return ReaderExt.Region_Conf.RG_PRC2;
            case 2:
                return ReaderExt.Region_Conf.RG_NONE;
            case 3:
                return ReaderExt.Region_Conf.RG_NA;
            case 4:
                return ReaderExt.Region_Conf.RG_EU3;
            default:
                return ReaderExt.Region_Conf.RG_NONE;
        }
    }
}